{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "# os.environ[\"CUDA_VISIBLE_DEVICES\"] = \"1\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# load dataset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Using TensorFlow backend.\n",
      "f:\\anaconda\\envs\\py37\\lib\\site-packages\\tensorflow\\python\\framework\\dtypes.py:526: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  _np_qint8 = np.dtype([(\"qint8\", np.int8, 1)])\n",
      "f:\\anaconda\\envs\\py37\\lib\\site-packages\\tensorflow\\python\\framework\\dtypes.py:527: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  _np_quint8 = np.dtype([(\"quint8\", np.uint8, 1)])\n",
      "f:\\anaconda\\envs\\py37\\lib\\site-packages\\tensorflow\\python\\framework\\dtypes.py:528: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  _np_qint16 = np.dtype([(\"qint16\", np.int16, 1)])\n",
      "f:\\anaconda\\envs\\py37\\lib\\site-packages\\tensorflow\\python\\framework\\dtypes.py:529: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  _np_quint16 = np.dtype([(\"quint16\", np.uint16, 1)])\n",
      "f:\\anaconda\\envs\\py37\\lib\\site-packages\\tensorflow\\python\\framework\\dtypes.py:530: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  _np_qint32 = np.dtype([(\"qint32\", np.int32, 1)])\n",
      "f:\\anaconda\\envs\\py37\\lib\\site-packages\\tensorflow\\python\\framework\\dtypes.py:535: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  np_resource = np.dtype([(\"resource\", np.ubyte, 1)])\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "from keras.utils import np_utils\n",
    "from keras.models import Sequential\n",
    "from keras.layers import Dense\n",
    "from keras.layers import Dropout\n",
    "\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(2032035, 12) (2032035, 1) (508012, 12) (508012, 1)\n"
     ]
    }
   ],
   "source": [
    "x_train = np.load('E:/IDS/alldata/12/RUS+SMOTE/label_2/data.npy')\n",
    "y_train = np.load('E:/IDS/alldata/12/RUS+SMOTE/label_2/label.npy')\n",
    "\n",
    "x_test = np.load('E:/IDS/alldata/12/test/data.npy')\n",
    "y_test = np.load('E:/IDS/alldata/12/test/label_2.npy')\n",
    "\n",
    "x_val = np.load('E:/IDS/alldata/12/val/data.npy')\n",
    "y_val = np.load('E:/IDS/alldata/12/val/label_2.npy')\n",
    "\n",
    "\n",
    "x_train=np.concatenate([x_train,x_val],axis=0)\n",
    "y_train=np.concatenate([y_train,y_val],axis=0)\n",
    "\n",
    "print(x_train.shape,y_train.shape,x_test.shape,y_test.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# RF"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "f:\\anaconda\\envs\\py37\\lib\\site-packages\\ipykernel_launcher.py:20: DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples,), for example using ravel().\n",
      "[Parallel(n_jobs=-1)]: Using backend ThreadingBackend with 8 concurrent workers.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Average number of nodes 75\n",
      "Average maximum depth 11\n",
      "train_time: 9.511723756790161\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[Parallel(n_jobs=-1)]: Done  25 out of  25 | elapsed:    9.2s finished\n"
     ]
    }
   ],
   "source": [
    "from sklearn.ensemble import RandomForestClassifier\n",
    "from sklearn.tree import DecisionTreeClassifier\n",
    "from sklearn.metrics import precision_score, recall_score, roc_auc_score, roc_curve\n",
    "import time\n",
    "# Create the model with 100 trees\n",
    "RSEED = 50   \n",
    "#binary-class\n",
    "model = RandomForestClassifier(n_estimators=25,  #树的个数，即学习器的个数\n",
    "                               random_state=RSEED,  #随机数生成器使用的种子\n",
    "                               max_features = 'sqrt',  #最大特征数 默认是none，划分时考虑所有的特征\n",
    "                               min_samples_split= 10,  #内部节点再划分所需最少的样本数\n",
    "                               max_leaf_nodes= 38,#最大叶子节点数\n",
    "                               max_depth=20,  #决策树的最大深度\n",
    "                               bootstrap = False,\n",
    "                               n_jobs=-1,  #用于并行提高效率，-1表示使用所有CPU，1表示不并行\n",
    "                               verbose = 1)  #int，可选，默认：0。用于开启/关闭迭代中间输出日志功能。\n",
    "time_start = time.time()\n",
    "\n",
    "#multi-class\n",
    "# model = RandomForestClassifier(n_estimators=157,  #树的个数，即学习器的个数\n",
    "#                                random_state=RSEED,  #随机数生成器使用的种子\n",
    "#                                max_features = 'auto',  #最大特征数 默认是none，划分时考虑所有的特征\n",
    "#                                min_samples_split= 5,  #内部节点再划分所需最少的样本数\n",
    "#                                max_leaf_nodes= 48,#最大叶子节点数\n",
    "#                                max_depth=27,  #决策树的最大深度\n",
    "#                                bootstrap = True,\n",
    "#                                n_jobs=-1,  #用于并行提高效率，-1表示使用所有CPU，1表示不并行\n",
    "#                                verbose = 1)  #int，可选，默认：0。用于开启/关闭迭代中间输出日志功能。\n",
    "# time_start = time.time()\n",
    "\n",
    "\n",
    "# Fit on training data\n",
    "model.fit(x_train,y_train)\n",
    "\n",
    "n_nodes = []\n",
    "max_depths = []\n",
    "for ind_tree in model.estimators_:\n",
    "    n_nodes.append(ind_tree.tree_.node_count)\n",
    "    max_depths.append(ind_tree.tree_.max_depth)\n",
    "print(f'Average number of nodes {int(np.mean(n_nodes))}')\n",
    "print(f'Average maximum depth {int(np.mean(max_depths))}')\n",
    "\n",
    "time_end = time.time()\n",
    "train_time = time_end - time_start\n",
    "print(\"train_time:\",train_time)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# test"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[Parallel(n_jobs=8)]: Using backend ThreadingBackend with 8 concurrent workers.\n",
      "[Parallel(n_jobs=8)]: Done  25 out of  25 | elapsed:    0.0s finished\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "test_time: 0.23464751243591309\n"
     ]
    }
   ],
   "source": [
    "time_start_test = time.time()\n",
    "y_pred  = model.predict(x_test)\n",
    "y_pred =y_pred.reshape(y_pred.shape[0],1)\n",
    "time_end_test = time.time()\n",
    "test_time = time_end_test - time_start_test\n",
    "print(\"test_time:\",test_time)\n",
    "\n",
    "y_true =y_test\n",
    "\n",
    "np.savetxt(\"E:/IDS/alldata/12/RUS+SMOTE/label_2/RF_y_pred_RUS.txt\",y_pred,fmt = '%s') \n",
    "np.savetxt(\"E:/IDS/alldata/12/RUS+SMOTE/label_2/RF_y_true_RUS.txt\",y_true,fmt = '%s')  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAABWEAAALrCAYAAACf5UV2AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAASdAAAEnQB3mYfeAAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy8QZhcZAAAgAElEQVR4nOzdeZQtZXU3/u9uZFJmZFAjjkRQVHCMUSOoGFERp2gAB0zeaF71VaMxQX8mOMQ4EAlOiXHEEY2JRsUQJ0DjEDBOgHEWHDAggso8CM/vj6rGPofue293n+L0PXw+a9U6t6ueqmdXncOi1+599lOttQAAAAAAMIy5aQcAAAAAADDLJGEBAAAAAAYkCQsAAAAAMCBJWAAAAACAAUnCAgAAAAAMSBIWAAAAAGBAkrAAAAAAAAOShAUAAAAAGJAkLAAAAADAgCRhAQAAAAAGdINpBwAAAAAArB1VddMk2087jt4vWms/nXYQq1WttWnHAAAAAACsAVV108xtelauvnLaocy7MMkeG3siViUsAAAAADBv+1x9ZTa91QGpzbadaiDtil/lyjOO3zpdVa4kLAAAAAAwO2qzbTO35Y5TjeHqqc4+WZKwAAAAAMComuu2accwI2bnTgAAAAAA1iCVsAAAAADAqKpum3YMM0IlLAAAAADAgCRhAQAAAAAGpB0BAAAAADDKwlwTNTt3AgAAAACwBknCAgAAAAAMSDsCAAAAAGBUJamafgwzQiUsAAAAAMCAJGEBAAAAAAakHQEAAAAAMKrmum3aMcyI2bkTAAAAAIA1SCUsAAAAADCmpr8w1wytzKUSFgAAAABgQJKwAAAAAAAD0o4AAAAAABhlYa6Jmp07AQAAAABYgyRhAQAAAAAGpB0BAAAAADCqklRNP4YZoRIWAAAAAGBAkrAAAAAAAAPSjgAAAAAAGFVz3TbtGGbE7NwJAAAAAMAapBIWAAAAABhVtQYW5pqdlblUwgIAAAAADEgSFgAAAABgQNoRAAAAAACjLMw1UbNzJwAAAAAAa5AkLAAAAADAgLQjAAAAAACurWraEcwMlbAAAAAAAAOShAUAAAAAGJB2BAAAAADAqJrrtmnHMCNm504AAAAAANYglbAAAAAAwCiVsBM1O3cCAAAAALAGScICAAAAAAxIOwIAAAAAYNRcddu0Y5gRKmEBAAAAAAYkCQsAAAAAMCDtCAAAAACAUVVJTbl+s7QjAAAAAABgA0jCAgCsR1W9qKpaVe07tr9V1UnTiWpUVR3Wx3PYtGNZqaratareUVU/qaqr+vvZbuA59+3nedGQ81yfVNUt+2d6zLRjAQBYK7QjAAAWVVWt/+ePktyutXbZImPOTHKLJJu21n59HYbHbDomyYOSHJvke0lakmt97hhWn8h/e5Int9aOmW40AMD01BpoBzDt+SdHEhYAWJ/dkjw7ySumHcgatGeSS6YdxCyoqs2S7J/kU621Q6/DqU9J9z7+/Dqcc9adle6Z/mragQAArBWSsADAuvwiXTXi86vqLa01iaoFWmvfmnYMM2TXdK2yfnpdTtpauySJ93GCWmtXxjMFgI1fza2Bhblmp5Pq7NwJADCES5K8NMk2SY5YzolV9diq+mxV/aqqLq2q06rq+VW1+SJjz+y3barqqP7fV8736VzYk7WqDq6qL1fVJVX103785v24+1fVSVV1QVX9oqreVVU7LjLfflX1pqr6n37spVV1elUdUVVbLOMeR3rCLugvuq5t37Fr7FFVx1TVj6vq8qo6p6reW1W3W2LO21bVB/r7u7iqvlBVD93QmMeutUlV/WlVfX7B+/S9qnpLVe0+Nnbbqnp5VX27qi7r5/94VT1wkete02e1qvauqo9V1S/79+wzVfW7Y+PPTPLD/scnLXhWx/THF+3J2x9btP9oVe1SVX/Xx3txP/+3+2d968ViXeTau1fVO6vqrKq6ov+8vXP82YzHWFWPqapT+vs9v6reV1U3W/KNuPa1runvW1X7V9V/VtVFVXVuVb29+j65VbVPVR3XvxcXVdVHquqWi1zvrlX1mqr6eh/PZVX13ap6dVVtPzb2pHStCJLk7WOf3Vsucq+HVNXJ/fxnLvWeVNU9+2f4g6radmzOm/Sf+4uqao8NfU4AABsTlbAAwPq8Ickzkjy1ql7XWvvO+k6oqr9N8vx0X/F+b5KLkhyQ5G+T/H5V7d9Xyy20WZITkuyQ5BNJLkhyxtiY/9df59+SnJSuf+ifJdmhqj6c5H1JPpbkTUl+N8njk9y4P2ehv0yyR5Iv9OO3SHLvJC9Ksm9VPbC1dtX67nMRZyZ58SL7N03ynH6ea9oXVNWDk3ywP/7RdH1QfyvJo5I8tKr2a619ZcH43ZN8McmOSY5P8rUkt033PI5fTqDVff3/Y0kemOTH6d6nC5LcMskjk3wuyXf7sdsl+XyS2yf5UpKj0z3Xxyb5RFX939baPy0yzd2S/EUf81vStbZ4dJJPV9XerbVv9+OO7ud9VpKv9/eT/v6Wrapu2Md7mySfTPdsK13/4oOS/EuSH6znGndP8qkkWyf5SJL/SfeZOTTJQVX1gNbafy9y6tOSPLw/5zNJ7pnkcUnu3N/z5cu4lYcneViS45K8Md1n+rAkt6qqw5N8Osl/JnlrkjsmOTDJbarqjq21qxdc50/Svaef6e9pkyR3SfeZPKCq7tlau7Afe0ySX6Z7Th/O6Hvwy7H4npuuhcRHk5yYZNssobV2clW9IMmRSd6c7rOTqppL8u4kOyc5THU5ADCrJGEBgHVqrV3ZJ3w+kK4v7KPWNb6q7pUuAfvjJPdorZ3d739+kg+lSyo9L11CdqGbpEt03a+1dvESl39gkru21r7ZX3PzJF9J8oR0CagHtdY+0x+bS/LxJA/uk18Lk0lPS3JGa60tvHhVvTTJC5M8Jsn713Wfi2mtnZkukTuirwjcIsnRrbVT+n3bp1uA6pIkv9da+58F4++Q5OR0icu7LLjUG9IlYJ/dWnvNgvEH5TeJyw31onTP86NJ/mBhcrB/rtssGPvKdAnYNyX50/nnVlWvTPLfSV5bVR/v73+hh2Zscaeqemq6hOKz0r0Paa0d3VdZPivJ11prL1rmvYx7QLoE7NGttT9beKBPPl+rGntsTCV5Z7pn8PjW2nsWHHtcumT/u6vq9mPJziR5cJK7t9ZOW3DOe5McnC6x+c/LuI+HJ3nAIp/pByb59yRPGYvtrUn+KN1/Cx9ecJ2XJ3n6+B8WquqP033GnpbuPU5r7Zju9nNQkn9bz8Jc909yr9baVzfwfl6dZN8kf1BVT+0T93/VX+ddrbV3bOB1AIDrQq2BhbmmPf8EaUcAAKxXa+1f0lUzPrKq7rOe4X/Uv/7NfAK2v8av01XOXZ3k/yxx7nPXkYBNktfOJ2D7a16eLlk6l+Rj88mq/tjV6SrskuTOY/fzg/EEbO/o/vX31xHDslTVXyd5Urqk2HMXHHpiku2SHLEwAdvH94101YL7VNXt++v8VrqqwzOSvH5s/IfTVTluaEybpEu8XZouqTpSndlau7y1dm4/dtN0FcUXJXn+wufWWvtuktemq2J+4iJTfX6RJN7bkvw6yT02NN5VuHR8R2vtigVVn0v53XRVr19cmOTsz39/uirh2yVZ7L+F1y5MwPbe3L8u956PXeQz/a7+x9PHY0uXOE6Svcdi/uESld1vS1f9vNLP+5uWkYBN/9k5LN3CXUdX1dPSJWG/neT/rjAGAICNgiQsALCh5hOIr+4rBZcyX7l5wviBvpXBT9J9nXq7scOXJTl1PTEs9vXv+YWcvrzIsbP6199auLOqblRVL6iqL1XXC/Xqqmrp2ickyQb371yXqjo0XXuC/05yyFjV5L361zv3PTZHtiS/3R/fs3/dp3/93BIJtZOWEdoe6b46fmprbX0LYe2R5IZJvt5aO3+R4/Pv8z6LHLvW+9W3oTgnyfbXHj4xn0n33h9eVf9RVc/s+6JusoHnL/kZHtu/Qfecrio8Wf49T+rzvmlVPaOqPtf3hL2q/7xfna7ad6Wf91OWe0K/uN8h6VpwvCHJlUket54/vgAAbPS0IwAANkhr7YtV9S/pvqr/2Cz9df35vpD/u8Tx/03XG3TbjPaY/NkS1akL/WqRfb/egGObzu/oKztPSFeVeHq6+zg3XTIo6RYgW+fX1TdEVd0vXaXhD5Mc2Fq7ZGzI/IJhf7KeS23Vv84/13OWGHf2EvsXM58AP2udo0bnXdf7ufCaC433EJ3363R9SQfRWrugqn4nXQL84flNpefPq+of0lVpj/ckXmjS9zz/OVzuPa/68957f7qesD9IV5F9dpL56udnZ+Wf9+V85hY6JcmPktwqyYmtta+v8DoAwJCqkppy/eYMtSOQhAUAluPwdL0iX15VH1pizHxyaNck31/k+E3Gxs1bXwJ2Ug5Kl4B9R2vtsIUHquom6ZKwq1JVt0vX//bSJA9Z2JZhgfn7v3NrbX0VwAvH77LE8V2XEeJ8onBDKiAXvp+LWer9nKT5CuLFfnddLBGa1tpPkvxxX7V9+3R9R5+e5K/TfRvsr9Yx31q454moqrulS8B+Kt1n8coFx+bSLZy2Uiv9b/Y16RKwP0+3MNihi7RWAACYKdoRAAAbrLX2/ST/kC6B8v+WGDbfI3Lf8QNVddt0X5U+o7W2VJXk0G7bv/7rIsfut9qLV9VO6RZN2irJo8f7vS7wX/3rfTfw0vPP9T5LfK1+3w0OMvlWukTsnarqpusZ++10i4ft3S8mNm6//vUry5h/uX7Rv958kWN3W9eJrfON1trr0vXUTZJHrGe+JT/DY/uHvOdJmf+8f2SR6t97JNlykXPm211MvFq5qv4gyVOSfDZd24dzk7yxqnaf9FwAAGuJJCwAsFwvSZfA+//ym6/KL/S2/vWFfUIyyTWLQf1dut8/3jp0kOtwZv+678KdVXXr9CvEr1RVbZHkI0luneSprbVPr2P429M9xyOq6loLNlXVXFVdE2Nf2fnJdAnwZ4yNPSjLSCD3PWX/IV0C7o1VNfJ19KrabP69a61dkeQ96d7rl4yNu02SZ6Zr5fCuDGe+9+iTq+qaatiqunm6ytYRVbVXVd1ykevMVxGPt4YY9/l0yef7VNVjxq79mCS/l+Q76RboWuvO7F/3XbizqnZO15N1Mef1r7tNMpD+v7E399c/tLX243QLut0oyfvHP4cAwJRVrY1tRmhHAAAsS2vt/Kr62ySvWuL4F6rqVem+5nx630f24iQHJNkrXeLqyOsq3kV8NMn3kjynqu6YrupxtyQPS/KxrC7x9Mwkv5Ou9+Yt+gW2xh3TWjuztXZen9D7UJL/qqpPJ/lGuq/e75Zu4a4dk2yx4NynJ/liupXlH5Tk6+kqHR/Z39eBy4j1xUnu2Z/znao6LsmF6apNH5TkeUmO6ccenq5i9xlVdfckJya5cbrewFsneUZr7YxlzL0srbWTq+qz6ZKfp1TVCekSqgcm+XiuXSH7wCRHVdUX0lX9/ixdBfZB6Z7vOj9/rbVWVU9Kl/R+f1V9uL/O7dJV0V6Y5IljC62tVV9Kl1R+VP88Ppfu2R2QLtG82MJsX0yXqH52Ve2Q3/Qhfl1rbUUtGPpezO9L12/3oP6PCmmt/UdVvTrJn6f7I81SFfYAABs1SVgAYCVem+RpSW652MHW2l9W1VfTVWw+Md1CQd9P8sIkr+6rK6eitXZxVd0/ySvSVQfeN13S9KVJjkryuFVc/ob9662zdG/Zk9JXJ7bWPl1Vd0qXgPr9PpYr0iXGTshYy4TW2nf7BadekS7RuG+SU9MlBnfKMpKwrbUrqurBSf403Xv0pCTVz/2hLKjy7BPv90ry/CSPSvKcdP1uT0lyZGvtExs67yoclC55elC6RN130yX6P5EuGbzQx5McnS5pe1CSbdItpvXJJEe11r6wvsn6xO/d031mH5ju2f48ybFJXtpa+/YE7mlwrbWrqurhSf4myUPS/aHgrCRv6fddq11Ga+0XVfXodJ/hJ6erVE2Sd2flfXBfkeTuSV7bWvvI2LEXpHuvnlFVJ7TWluo3DQBcl2puDSzMNTtf4q/1L0IMAAAAAFwfVNUdkpy+2b2fl7mtlrP26+RdfdHZueLzRybJXq21b0w1mFWanXQyAAAAAMAapB0BAAAAADBmLSyMNe35J0clLAAAAADAgNZUEraqDquqVlWXVdUtFjl+UlWdPo3YVmvBvd1y2rEAAAAAANedNZWEXWDzdKu1ArBcVTul6mOpujhV30nV/kuMu0Wq/j1Vv0zVGal67NjxZ6bqh6n6Vao+m645OwDA5Gz47y1bpurdqbowVT9K1eMXHHtoqr7Q/87y01QdlSqt9wBWqyqpuSlv2hEM7T+SHFJVdx5qgqracqhrA0zZG5KcnWSnJH+e5J9TteMi496d5OtJbpzkSUmOSdUeSZKquyV5WZIDk+yQ5MQkxwwdOABwvbOhv7e8ON3vLDdL8tgkr0/Vnv2xbZK8KMmuSfZJcu8kzx02bABYnrWahH1VkvOSvHJdg6pqi6p6eVWdUVVXVNVZVfWGqtpubNyZVXVcVT2qqr5aVZclOaI/1qrq9VX15Kr6dlVdWlX/XVW/U53n9de/qKpOqKrbjl17/6r6cFX9pG+j8L2q+qequvGEnwnA+lVtleQRSY5Ia5ektY+kS7QeNDZu6yT3SfLytPbrtPbZJP+ZZL6q5BZJTk1rp6a1q5K8J8ntr6O7AACuDzb095bOE5K8NK1dkNb+K8m/JTk4SdLasWntE2nt0rR2TrrfW37nOrkHgFlWtTa2GbFWv6JxYbp2BK+pqvu31k4YH1BVle5/vA9I8vJ0yYM7pfsL6b2q6l6ttcsXnHKXJHv21z0jycULjj0s3V9MD0/S0iV/P5bkHUluneQZSbZNclSSf62qvVtrrT/3Nkm+mOQtSX6V5JZJnpPkc1V1x9balat7FADLsnuSi9LaTxbsOy3JhrQSqAXjPpnk+am6S5JTkzwxyScmGSgAcL23Yb+3VG2frsr1tLFxv7vEde+V5BuTCxMAVm+tJmGT5I1JnpXklVV1jwVJz3kPSvL7Sf6itXZkv++TVfXjJO9PlzB484LxOye5fWvtO4vMtXmSB7XWLk666th0Cd79ktxlfu6q2inJ0Un2Sv8LQGvtjfMX6RPDX0hyUpIfJjkgyUdWdPcLVNW2Se6X5MdJrljt9YDZ9Z5kr8ckl26+oH/r95LNtkx2udlYT9cLk6+fmxy1X9XrXpXs85hk3wuSL21fdYcbJfWV5Iu7J6ckyZXJuS9JnvwyfWEBgAnZ0N9bnpns+pokmyU3v7KviDol2Wr3ZJftx343+XSy/32T+x+avP4Dfm8B1m+zJDdP8pnW2q+mHQyzbc0mYVtrV1TVC5O8N13Pn/ePDbl//3rM2P4PJHlbugrZhUnYU5dIwCbJifMJ2N43+9fjx5K/8/tvkT4JW1U7J3lJkocmuWlGWzzsmQkkYdMlYD88gesAM+7IJA/u/nn6/L5/zzV/vfmDhWPvlOQfkzv/d/LHX0tybJLLu4qS0w9N8ut0pf4/TPKYZNdXJscfleTSge8BALh+2NDfW96Z5DVJtkhOn/+a4ftyTRnsNefum66E9h5JvpZ8bpiogRl1UCaTv5kt84tjTTuGGbFmk7C996Vrzv6yqvrg2LEdk/y6tXbuwp2ttVZVZ/fHF/rfdcxz/tjPV6xn/xZJUlVz6b6ee9MkL02XmL04XSL2v5JMavGvHyfJDW79sMxtsd36xgLXYz+8+sps9a0P5Fa7H5SzNr1RkuSOZ34y7932Vtls+5GW1jkrycMX/HzCGR/PsdveKpvt8NvZ539PyXFzm+asXfbJDdJ9NeA13/6X3Hm3/fKVLRdbKwOgs8v2W+R5j7tT7nSXu+eGN9pq2uEAa9jcJZdku/vdO1897vhcucuuSZLb/ckf5byHHpjfe8QjR8Zesf9+OfnIo3LR3vskSW55xAtzxS675pSnPSNJcqPTTs3uz/p/+d6Rr86b7nq36/ZGgI3WN047NU9+4qFJn3eBIa3pJGyfUP3LdL0JnzJ2+LwkN6iqnRYmYvuWALsm+dL45QYIca8kd05yWGvtHQtiuO3Sp6zIFUkyt8V2mZP8ANbh0iTHbXfr/NX538lzdts3+13wo9z58l/m8TvfKXM3GP270G9fdn5+sunWuboqf3zuabn5VZflXTe5e+bmbpAvb3OL/Nk5X85bNrlHfrzp1nn0L76brdpVOXOb3TJ3gy2mc3PARmHzrW+Y3XbbLXvssWe22nqbaYcDrHFXP/wRucP7j80Vf/+6bHLCp7P5976bTZ7yp9n5xmPrHD/hSdn92Pfm8gMPyty3vpktPvuZXHrS57LdnrdPnX5atnzus3P5W96e3R524HRuBNgoXXLxRfP/1PqRwa35mt7W2qfSJWH/OsnCcopP96+PHzvl0UlutOD4kOYTu5eP7X/qdTA3wKKetdt+ucmVF+Wsr/1TXvWTz+YJtz4g591gy/zhed/Kl7/xrmvGPehXP8y3T3tbfvz1N+WAX52Rh+3+yFwx1/1t7t077pnjtrt1TvzWB3LO196Yvzj7S3nCrQ/ILyVgAYAJuvy1b0j99H9zw5vcOJs97zm5/N3vS25842xy7Huy5d57XTPuyiNekuywQ254i5tm8z98TK446jVpe94+SbLpa/4+Oe+8bP7EQ3LDHbbODXfYOpsf+JBp3RLA7Kj6TUuCqW017acwMWu6EnaBv0zy5XSLa82vcvnJJB9Pt3DXNkk+n67F4YuTfDXJuxa5zqR9K8n3k7yir8A9P8mBSfa/DuYGWNTPN71hHrn7I661/3077pH37bjHNT+/fpd98vpd9ln8IlU54mb3zhE3u/dQYQIAJDvtlMs/8rFr7b7q4ENz6cGH/mbHllvm8ne8e9FLXPHmt+WKN79tqAgBYCLWfCVskrTWvppuzZiF+1qSRyQ5KsmT0/Vw//N0ydf7t9bGq1OHiOvKdEnX7yT5pz7GnZM8cOi5AQAAAICNw5qqhG2tHZPkmCWOHZrk0LF9lyU5vN/Wdd1bruPYteqaW2tnJlls/0nj+1tr30zyoEUuPT7umCxxbwAAAACwplRNvx3AtOefoI2iEhYAAAAAYGO1piphAQAAAIC1oF8ca9oxzIjZuRMAAAAAgDVIEhYAAAAAYEDaEQAAAAAAoyzMNVEqYQEAAAAABiQJCwAAAAAwIO0IAAAAAIBRVUlNuX5TOwIAAAAAADaEJCwAAAAAwIC0IwAAAAAARlVNvx3AtOefIJWwAAAAAAADUgkLAAAAAIyoqtSUK1GnPf8kqYQFAAAAABiQJCwAAAAAwIC0IwAAAAAARmhHMFkqYQEAAAAABiQJCwAAAAAwIO0IAAAAAIBrm51uAFOnEhYAAAAAYECSsAAAAAAAA9KOAAAAAAAYUVWpmm4/gmnPP0kqYQEAAAAABqQSFgAAAAAYtQYqYTPt+SdIJSwAAAAAwIAkYQEAAAAABqQdAQAAAAAwomr6C2PNUDcClbAAAAAAAEOShAUAAAAAGJB2BAAAAADAiKpaA+0IZqcfgUpYAAAAAIABScICAAAAAAxIOwIAAAAAYFT127RjmBEqYQEAAAAABqQSFgAAAAAYUVkDC3PNUCmsSlgAAAAAgAFJwgIAAAAADEg7AgAAAABgRNUaaEcw5fknSSUsAAAAAMCAJGEBAAAAAAakHQEAAAAAMEI7gslSCQsAAAAAMCCVsAAAAADAqDVQCZtpzz9BKmEBAAAAAAYkCQsAAAAAMCDtCAAAAACAUdVv045hRqiEBQAAAAAYkCQsAAAAAMCAtCMAAAAAAEZUkqrp9gOYoW4EKmEBAAAAAIYkCQsAAAAAMCDtCAAAAACAEVU1/XYEU55/klTCAgAAAAAMSCUsAAAAADBCJexkqYQFAAAAABiQJCwAAAAAwIAkYQEAAACAUbVGtpWGX7VVVR1dVT+tqsuq6mtV9YcbeO5+VfXJqvpZVV1UVadW1TOrapOVxqMnLAAAAAAwaz6Y5O5JDk/ynSSHJDm2quZaa+9d6qSqemCSjyf5bJI/SXJxkocneU2S2yR51kqCkYQFAAAAAGZGVT0kyf5JDmmtHdvvPrGqbpHkyKp6f2vtqiVOPyzJlUke1lq7uN/3qaq6XX9sRUlY7QgAAAAAgBFVtSa2FXpkkouSfGBs/9uT3DTJPddx7pVJrkhy6dj+Xya5bKUBScICAAAAAGvZbarqDmPbzusYv1eSb7bWfj22/9QFx5fyxiSbJXltVd20qrarqiekS+y+aqU3oB0BAAAAALCWfXiRfS9O8qIlxu+Y5AeL7D9/wfFFtdZOrqr7p6uifXq/+6okz2+tvXqDol2EJCwAAAAAMGKV7QAmFkPvoCTfHzt87npObys5VlV3TfKhJCcneWq6hbnun+RvqmqL1tpL1zPvoiRhAQAAAIC17PuttW8sY/x5WbzadYf+9fxFjs17Q5JzkjxyweJdJ1bV1UleVFXvaa0tVmW7TnrCAgAAAABj1sKiXCuuxD0tyZ5VNV6Aesf+9fR1nLt3ki8vSMDO+1K6XOqeKwlIEhYAAAAAmCUfSrJVkkeP7X9Skp+mazWwlJ8muVtVbTK2/179609WEpB2BAAAAADAzGitHV9Vn0zyj1W1TZLvJTk4yYOTPH6+yrWq3pouMXub1toP+9P/Pslrk3y0qv4pySVJHpDkuUk+1Vr7+kpikoQFAAAAAEZUjSyMNbUYVuFRSV6W5CXpesF+K8nBrbX3LRizSb9dM1Nr7XVVdVaSP0vyliRbJjkzyYvTJWhXRBIWAAAAAJgprbWLkjyr35Yac1iSwxbZ/8EkH5xkPHrCAgAAAAAMSCUsAAAAADCqsuBL+lOMYUaohAUAAAAAGJAkLAAAAADAgLQjAAAAAABGVFWqptsPYNrzT5JKWAAAAACAAamEBQAAAABGqISdLJWwAAAAAAADkoQFAAAAABiQdgQAAAAAwAjtCCZLJSwAAAAAwIAkYQEAAAAABqQdAQAAAABwbbPTDWDqVMICAAAAAAxIEhYAAAAAYEDaEQAAAAAAI6oqVdPtRzDt+SdJJSwAAAAAwIBUwgIAAAAAo9ZAJWymPVJH21gAACAASURBVP8EqYQFAAAAABiQJCwAAAAAwIC0IwAAAAAARlRNf2GsGepGoBIWAAAAAGBIkrAAAAAAAAPSjgAAAAAAGFFVa6Adwez0I1AJCwAAAAAwIJWwAAAAAMCo6rdpxzAjVMICAAAAAAxIEhYAAAAAYEDaEQAAAAAAIyprYGGuGepHoBIWAAAAAGBAkrAAAAAAAAPSjgAAAAAAGFG1BtoRTHn+SVIJCwAAAAAwIElYAAAAAIABaUcAAAAAAIyo6rZpxzArVMICAAAAAAxIJSwAAAAAMGoNLMw1S6WwKmEBAAAAAAYkCQsAAAAAMCDtCAAAAACAERbmmiyVsAAAAAAAA5KEBQAAAAAYkHYEAAAAAMCIrh3BdPsBaEcAAAAAAMAGkYQFAAAAABiQdgQAAAAAwIjK9NsBzFA3ApWwAAAAAABDUgkLAAAAAIyYm6vMzU23FnXa80+SSlgAAAAAgAFJwgIAAAAADEg7AgAAAABgVE1/Ya5ZWplLJSwAAAAAwIAkYQEAAAAABqQdAQAAAAAwoqpSU+5HMO35J0klLAAAAADAgCRhAQAAAAAGpB0BAAAAADCiqtumHcOsUAkLAAAAADAglbAAAAAAwIjKGliYK7NTCqsSFgAAAABgQJKwAAAAAAAD0o4AAAAAABhRtQbaEczQylwqYQEAAAAABiQJCwAAAAAwIO0IAAAAAIARVd027RhmhUpYAAAAAIABScICAAAAAAxIOwIAAAAAYFRVatr9AKY9/wSphAUAAAAAGJBKWAAAAABghIW5JkslLAAAAADAgCRhAQAAAAAGpB0BAAAAADCia0cw3X4A2hEAAAAAALBBJGEBAAAAAAakHQEAAAAAMKIy/XYAM9SNQCUsAAAAAMCQVMICAAAAACOqag0szDU7tbAqYQEAAAAABiQJCwAAAAAwIO0IAAAAAIARVWtgYa7Z6UagEhYAAAAAYEiSsAAAAAAAA9KOAAAAAAAYVZWadj+Aac8/QSphAQAAAAAGJAkLAAAAADAg7QgAAAAAgBFV0+8GMO35J0klLAAAAADAgFTCAgAAAAAjag0szDXt+SdJJSwAAAAAwIAkYQEAAAAABqQdAQAAAAAwojL9hbFmpxmBSlgAAAAAgEFJwgIAAAAADEg7AgAAAABgRFWlptyPYNrzT5JKWAAAAACAAUnCAgAAAAAMSDsCAAAAAGBEVbdNO4ZZoRIWAAAAAGBAKmEBAAAAgFFrYGGuWSqFVQkLAAAAADAgSVgAAAAAgAFpRwAAAAAAjOgW5ppuO4AZ6kagEhYAAAAAYEiSsAAAAAAAA9KOAAAAAAAY0bUjmH4Ms0IlLAAAAADAgCRhAQAAAAAGpB0BAAAAADCiUqkp9wOozE4/ApWwAAAAAAADUgkLAAAAAIywMNdkqYQFAAAAABiQJCwAAAAAwIC0IwAAAAAARlStgYW5ZqgfgUpYAAAAAIABScICAAAAAAxIOwIAAAAAYFQlU+8GMO35J0glLAAAAADAgCRhAQAAAAAGpB0BAAAAADBiripzU+5HMO35J0klLAAAAADAgFTCAgAAAAAjag0szDXt+SdJJSwAAAAAwIAkYQEAAAAABqQdAQAAAAAwolKpKfcDqMxOPwKVsAAAAAAAA5KEBQAAAAAYkCQsAAAAADCiKpmb8raabghVtVVVHV1VP62qy6rqa1X1h8s4/6Cq+kxVXVBVF1fVN6rqKSuNR09YAAAAAGDWfDDJ3ZMcnuQ7SQ5JcmxVzbXW3ruuE6vq8CQvS/LGJC9PcmWSPZJsttJgJGEBAAAAgJlRVQ9Jsn+SQ1prx/a7T6yqWyQ5sqre31q7aolz75ouAfv81tqrFhz69Gpi0o4AAAAAABhRVWtiW6FHJrkoyQfG9r89yU2T3HMd5z4jyeVJXrfSyRcjCQsAAAAAzJK9knyztfbrsf2nLji+lN9L8s0kj66qb1fVVVX1k6p6RVVpRwAAAAAATEatcmGsScXQu80iVbHnttZ+tsSpOyb5wSL7z19wfCk3S7JTktcm+ask/5PkAel6y948yaHri3sxkrAAAAAAwFr24UX2vTjJi9ZxTlvhsbkkWyc5uLX2vn7fiVV1oyTPrqojWmvfW1ewi5GEBQAAAADWsoOSfH9s37nrGH9eFq923aF/PX+RYwvP3TXJx8f2H5/k2UnukkQSFgAAAABYnUpSmW4/ggWzf7+19o1lnHpakoOr6gZjfWHv2L+evo5zT02XhF0qnKuXEcc1LMwFAAAAAMySDyXZKsmjx/Y/KclPk5y8jnP/tX89YGz/Q9IlYL+0koBUwgIAAAAAM6O1dnxVfTLJP1bVNunaBxyc5MFJHt9auypJquqt6RKzt2mt/bA//e1JnprkH6rqxukW5npgkqcn+YcF45ZFEhYAAAAAGDFX3TbtGFbhUUleluQl6XrBfiuji20lySb9ds1MrbUrq2r/JH+b5AX9uWckOTzJUSsNRhIWAAAAAJgprbWLkjyr35Yac1iSwxbZf36SP+23iZCEBQAAAABGVaVqyqWw055/gizMBQAAAAAwIElYAAAAAIABaUcAAAAAAIyoTL8bwOw0I1AJCwAAAAAwKElYAAAAAIABaUcAAAAAAIyYq8rclPsRTHv+SVIJCwAAAAAwIElYAAAAAIABaUcAAAAAAIyo6rZpxzArVMICAAAAAAxIJSwAAAAAMKKqUlMuRZ32/JOkEhYAAAAAYECSsAAAAAAAA9KOAAAAAAAYtQYW5sq0558glbAAAAAAAAOShAUAAAAAGJB2BAAAAADAiLmqzE25H8G0558klbAAAAAAAAOShAUAAAAAGJB2BAAAAADAiOq3accwK1TCAgAAAAAMSCUsAAAAADCiUqkpL4xVM1QLu6wkbFXtvJzxrbWfLS8cAAAAAIDZstxK2LOTtGWM32SZ1wcAAAAAmCnLTcI+LctLwgIAAAAAG5mqZG7K3QCm3A1hopaVhG2tvXGoQAAAAAAAZtHcJC5SVZtU1Y5VNZHrAQAAAADMilUlTavqd6vqxCSXJDknyd79/r+vqodPID4AAAAA4DpWlVTVlLdpP4XJWXEStqrum+TEJLsk+cckCx/LRUn+aHWhAQAAAABs/FZTCfs3ST6dZK8kf57RJOzXkuyzimsDAAAAAMyEZS3MNeZuSR7bWru66lrFwT9LsvMqrg0AAAAATEnXjmD6McyK1VTCXrWO83dKcvEqrg0AAAAAMBNWUwn75SSHJPnoIscekeTkVVwbAAAAAJiS+cWxph3DrFhNEvZVSY7rH8Y7+337VNUhSQ5Osv8qYwMAAAAA2OitOAnbWju+qp6S5NVJHtvvfnOSi5I8tbV20urDAwAAAADYuK2mEjattbdW1T8nuV+6hbh+nuQzrbVfTSI4AAAAAOC6V5XMWZhrYlaVhE2S1tqFSY6bQCwAAAAAADNnVUnYqrpRkqck2S/JjknOS3Jikje31i5afXgAAAAAABu3FSdhq+rm6RKut05yTpKz+38/LMkzqmq/1tqPJhIlAAAAAHCdqVRqyv0AKrPTj2BuFee+Jsm2SR7QWrtJa22f1tpNkjwwyTZJjp5EgAAAAAAAG7PVJGH3T/KC1tqJC3e21k5I8sL+OAAAAADA9dpqesJemeTMJY6d0R8HAAAAADYy1W/TjmFWrKYS9rgkj1zi2COTHL+KawMAAAAAzIRlVcJW1e0X/PjWJO+sqncleW+6hbl2TXJokvskeeKkggQAAAAArjtzVZmb8sJc055/kpbbjuD0JG3Bz5Uu6XrI2L4kOTHJJisPDQAAAABg47fcJOzTMpqEBQAAAABgHZaVhG2tvXGoQAAAAACAtaGq26Ydw6xYzcJcAAAAAACsx3LbEYyoqq2TPC7Jnkm2HDvcWmtPX831AQAAAAA2ditOwlbVzZKckmSHJJsluSDJtv3hC5NcnEQSFgAAAAA2NlWpafcDmPb8E7SadgQvT/L9JDsnqSQPSLJ1kucl+VWS+606OgAAAACAjdxq2hHcJ8nhSS7pf67W2sVJXl1VOyb5uyQHrTI+AAAAAOA6ZmGuyVpNJexNkvy0tXZVkqvSVcHOOyEqYQEAAAAAVpWEPSfJ9v2/f5TkLguO/Va6xCwAAAAAwPXaatoRnJxk7yQfTfJvSY6oqrkkVyR5QZKTVh0dAAAAAHCdm6vK3JT7AUx7/klaTRL2qCS36v/9oiR3TPKq/udTkjxrFdcGAAAAAJgJK07CttZOTlcNm9bahUkeVFU7dT+2n08oPgAAAACAjdpqKmGvpbV27iSvBwAAAABc9yrJtLsBzE4zgmUmYavqHssZ31o7ZXnhAAAAAADMluVWwv5XkrYB46oft8myIwIAAAAAmCHLTcIeMEgUbJAvvvfw3P4Od5h2GAAAS7rowgvy+c+emPsc/Lf5ybkXTzscAIAl7bTlpdMOYU2rSmrK/Qim3Q5hkpaVhG2tfXyoQFi/Sy6+KBddeMG0wwAAWNIlF3eJ11122HLKkQAArNvWm1ydH087CK43JrowF8P66pdPyXnnnj3tMAAA1ut5B+8z7RAAANbpRz/6Ub76H9OOYu2qJHNrIIZZIQm7EdnnrvfIHnvuOe0wAACWdMnFF+erXz4lRx771Zxzvq/4AQBr19abXDjtELgekYTdiNzwRltlq623mXYYAADrdc75l+oJCwCsaTttefm0Q+B6RBIWAAAAABhRVWtgYa7ZaUgw7dYOAAAAAAAzTRIWAAAAAGBAq0rCVtVtqurtVXVGVV1QVXv3+59fVfedTIgAAAAAwHVprtbGNitWnIStqr2SfCXJQ5N8LcmNFlzvxkmeturoAAAAAAA2cquphH1lkm8luU2SP0iyMDd9cpJ7rOLaAAAAAAAz4QarOPe+SZ7UWruwqjYZO3Z2kpus4toAAAAAwJTUGmgHUNoRJEk2SXLpEse2S3LFKq4NAAAAADATVlMJe3qSA5P8xyLHHpTkq6u4NgAAAAAwJVWVmnIp6rTnn6TVJGFfl+QdVXVBknf1+3apqmck+T9JDlltcAAAAAAAG7sVJ2Fba++uqt9O8vwkf9HvPi7J1Ule1lr74ATiAwAAAADYqK2mEjattb+uqmOSHJBklyQ/T3J8a+27E4gNAAAAAJiCuUx/Ya7VLGa11qwqCZskrbUfJHnDBGIBAAAAAJg5K07CVtXO6xvTWvvZSq8PAAAAADALVlMJe3aStp4xm6zi+gAAAADAFFR127RjmBWrScI+LddOwt44yYFJdkpy5CquDQAAAAAwE1achG2tvXGJQy+rqn9Lsv1Krw0AAAAAMCuGWmTsrUmeOtC1AQAAAIABVVXmprzVDPUjGCoJmyQ7DnhtAAAAAICNwmp6wi6qqu6Q5Igkp0362gAAAADw/7d353GWVdW9wH+rGmQUFHCIUUQR4wAap2g0KmoUnFBxBEkY1KcZHDD6nkFUcIpRo0aNmmdAjIoSo8ZZHB5OcYojo4ogIKPQoNACzdD7/XFuY93qqqa7uk6d7lvf7/3sz7337H3OWbc+nx5q1aq16d9U+q3eXNcYJsW8k7BVdVXW3JhrsyTLklyRZO8NiAsAAAAAYCJsSCXsP2fNJOzVSc5K8qnW2mUbcG0AAAAAgIkwryRsVU0leVeSy1prVyxsSAAAAADAkKq6MXQMk2K+rRWWJTkzyUMWMBYAAAAAgIkzryRsa+3aJL9OsmphwwEAAAAAmCwb0hP2o0memeTzCxQLAAAAALARmKrK1MD9AIa+/0LakCTsfyd5c1V9LsnHk1yQGRt1tdY+twHXBwAAAADY5G1IEvYjo+fbJtl72vGWpEbPyzbg+gAAAAAAm7wNScI+esGiAAAAAAA2KhPUDWBw65WEraqHJPlha21Fa+34nmICAAAAAJgY61sJe0KSP03yvR5iAQAAAAA2AlPVjaFjmBRT67l+gj46AAAAAED/1jcJCwAAAADAepjPxlxtwaMAAAAAADYaU1WZGnhnrqHvv5Dmk4Q9oapWrcO61lrbfh7XBwAAAACYGPNJwn41ycULHAcAAAAAwESaTxL21a217y14JAAAAADARqGqG0PHMClszAUAAAAA0KP5VMICAAAAABOsKplSCbtgVMICAAAAAPRovSphW2uStgAAAAAA60E7AgAAAABgTCWpDNsPYIK6EWhHAAAAAADQJ0lYAAAAAIAeaUcAAAAAAIyZqm4MHcOkUAkLAAAAANAjSVgAAAAAgB5pRwAAAAAAjKkM3w5ggroRqIQFAAAAAOiTSlgAAAAAYExVpWrYWtSh77+QVMICAAAAAPRIEhYAAAAAoEfaEQAAAAAAY6Zq+I25hr7/QlIJCwAAAADQI0lYAAAAAIAeaUcAAAAAAIyp6sbQMUwKlbAAAAAAAD2ShAUAAAAA6JF2BAAAAADAmKrK1MD9AGqC+hGohAUAAAAA6JFKWAAAAABgzFR1Y+gYJoVKWAAAAACAHknCAgAAAAATpaq2raq3VdX5VXV1Vf24qp4xj+u8tqpaVZ28IfFoRwAAAAAAjKkkQ++LtYG3/3iS+yV5WZKfJ9k/yYeraqq1duw63b/qj5O8JMlFGxaKJCwAAAAAMEGq6jFJHplk/9bah0eHT6iq2yd5U1Ud11q7/kausVmS9yX51yT3TLLThsSkHQEAAAAAMEmelGRFko/OOP6+JLdJcv91uMbLkuyQ5OULEZBKWAAAAABgzFQqUxvaEGABYhjZtdbsjXBxa+3Xc5y6e5LTWmvXzTh+4rT5b81136q6W5LDk+zbWlsxy73XmyQsAAAAALAx++Qsx45McsQc63dMcuYsxy+dNj+rqppKcnSSj7fWPrceMa6VJCwAAAAAsDF7QpIzZhy7+EbOafOce3GS3ZLssw5xrTNJWAAAAABgXCUL8Fv4GxzDyBmttVPW48zlmb3adYfR86WzzKWqdk7y6nT9YK+pqpuNpjZLMjV6v7K1dtV6xJLExlwAAAAAwGQ5Kcldq2pmAeoeo+eT5zjvjkm2SvLPSS6bNh6U5K6j1/8wn4BUwgIAAAAAY6aqG0PHME+fSPKcJE9Octy04wcmOT/Jd+c478dJHjbL8bcl2T7JwUnOnU9AkrAAAAAAwMRorX2+qr6U5N1VtV2SXyTZL8neSQ5orV2fJFV1VLrE7K6ttbNba79J8tWZ16uq3yTZrLW2xty6koQFAAAAACbNvklel67H6w5Jfppkv9baR6atWTYavdf8SsICAAAAAGOmqjI18M5cG3L/1tqKJC8cjbnWHJTkoHW41p7zDmTExlwAAAAAAD2ShAUAAAAA6JF2BAAAAADAmKpuDB3DpFAJCwAAAADQI0lYAAAAAIAeaUcAAAAAAIypqkwN3A+gJqgfgUpYAAAAAIAeqYQFAAAAAMZUht8Ya3LqYFXCAgAAAAD0ShIWAAAAAKBH2hEAAAAAAGOmMnz15tD3X0iT9FkAAAAAADY6krAAAAAAAD3SjgAAAAAAGFOVVNXgMUwKlbAAAAAAAD1SCQsAAAAArGGCClEHpxIWAAAAAKBHkrAAAAAAAD3SjgAAAAAAGDNVlamBd8Ya+v4LSSUsAAAAAECPJGEBAAAAAHqkHQEAAAAAMKZGY+gYJoVKWAAAAACAHknCAgAAAAD0SDsCAAAAAGBMVTeGjmFSqIQFAAAAAOiRSlgAAAAAYExVpQYuRR36/gtJJSwAAAAAQI8kYQEAAAAAeqQdAQAAAAAwpjJ89ebkNCMY/msJAAAAADDRJGEBAAAAAHqkHQEAAAAAMKaqUjVsQ4Ch77+QVMICAAAAAPRIEhYAAAAAoEfaEQAAAAAAY2o0ho5hUqiEBQAAAADokUpYAAAAAGBMZSPYmGuCamFVwgIAAAAA9EgSFgAAAACgR9oRAAAAAABjKsNXb05OM4Lhv5YAAAAAABNNEhYAAAAAoEfaEQAAAAAAY6oqVcM2BBj6/gtJJSwAAAAAQI8kYQEAAAAAeqQdAQAAAAAwpkZj6BgmhUpYAAAAAIAeqYQFAAAAAMZUdWPoGCaFSlgAAAAAgB5JwgIAAAAA9Eg7AgAAAABgzFSSqYG3xpqk6tFJ+iwAAAAAABsdSVgAAAAAgB5pRwAAAAAAjKukhu1GkIG7ISwolbAAAAAAAD2ShAUAAAAA6JF2BAAAAADAmBo9ho5hUqiEBQAAAADokUpYAAAAAGBMbQQbcw19/4WkEhYAAAAAoEeSsAAAAAAAPdKOAAAAAAAYM5XK1MAbYw19/4WkEhYAAAAAoEeSsAAAAAAAPdKOAAAAAAAYV0kN3Q1g6PsvIJWwAAAAAAA9koQFAAAAAOiRdgQAAAAAwJjK8O0IJqgbgUpYAAAAAIA+qYQFAAAAAMbU6DF0DJNCJSwAAAAAQI8kYQEAAAAAeqQdAQAAAAAwZqq6MXQMk0IlLAAAAABAjyRhAQAAAAB6pB0BAAAAADCmRo+hY5gUKmEBAAAAAHqkEhYAAAAAGFdJDV2IOvT9F5BKWAAAAACAHknCAgAAAAD0SDsCAAAAAGCMjbkWlkpYAAAAAIAeScICAAAAAPRIOwIAAAAAYMxUdWPoGCaFSlgAAAAAgB5JwgIAAAAA9Eg7AgAAAABgTCWpDNsPYIK6EaiEBQAAAADok0pYAAAAAGBMVTeGjmFSqIQFAAAAAOiRJCwAAAAAQI+0IwAAAAAA1jBB3QAGpxIWAAAAAKBHkrAAAAAAAD3SjgAAAAAAGDNVlakatiHB0PdfSCphAQAAAAB6JAkLAAAAANAj7QgAAAAAgDE1GkPHMClUwgIAAAAA9EglLAAAAACwpkkqRR2YSlgAAAAAgB5JwgIAAAAA9Eg7AgAAAABgTI0eQ8cwKVTCAgAAAAD0SBIWAAAAAKBH2hEAAAAAAGOqujF0DJNCJSwAAAAAQI8kYQEAAAAAeqQdAQAAAACwhgnqBjA4lbAAAAAAAD1SCQsAAAAAjKsMXwo79P0XkEpYAAAAAIAeScICAAAAAPRIOwIAAAAAYEyNHkPHMClUwgIAAAAA9EgSFgAAAACgR9oRAAAAAABjqroxdAyTQiUsAAAAAECPJGEBAAAAAHqkHQEAAAAAMKZGY+gYJoVKWAAAAACAHqmEBQAAAADGKYVdUCphAQAAAAB6JAkLAAAAANAj7QgAAAAAgDE1egwdw6RQCQsAAAAA0CNJWAAAAABgolTVtlX1tqo6v6qurqofV9Uz1uG8favqw1X1i6q6qqrOqqoPVdVuGxKPdgQAAAAAwJiqbgwdwwb4eJL7JXlZkp8n2T/Jh6tqqrV27FrO+z9JLkzyuiRnJrldksOS/LCqHtBaO2U+wUjCAgAAAAATo6oek+SRSfZvrX14dPiEqrp9kjdV1XGttevnOP3xrbVfz7je/0tyVpJDkzx7PjFpRwAAAAAArKEGHhvgSUlWJPnojOPvS3KbJPef68SZCdjRsfOTnJuuKnZeJGEBAAAAgEmye5LTWmvXzTh+4rT5dVZVd0xy+yTzakWQaEcAAAAAAGzcdq01G8RePFvV6siO6fq5znTptPl1UlWbJTkqXWXtW9f1vJkkYQEAAACANQ28Mdc0n5zl2JFJjljLOW2eczeoLvN7VJIHJ3lya+1X63LebCRhAQAAAICN2ROSnDHj2MVrWb88s1e77jB6vnSWuTGjBOy/JTkgyYGttdkSwetMEhYAAAAA2Jid0Vpbn36sJyXZr6o2m9EXdo/R88lrO3laAvbgJM9qrX1wvaKdhY25AAAAAIAxtZE85ukTSbZN8uQZxw9Mcn6S7875ubsE7HvTJWCf21p733yDmE4lLAAAAAAwMVprn6+qLyV5d1Vtl+QXSfZLsneSA1pr1ydJVR2VLjG7a2vt7NHpb0/yrCRHJzmpqh4w7dIrW2s/mk9MkrAAAAAAwKTZN8nrkrw6XS/YnybZr7X2kWlrlo3G9JLbx4+eDxmN6c5Osst8gpGEBQAAAADGVHVj6Bjmq7W2IskLR2OuNQclOWjGsV3mf9e56QkLAAAAANAjlbAAAAAAwJhK5r8t1gLGMClUwgIAAAAA9EgSFgAAAACgR9oRAAAAAADj9CNYUCphAQAAAAB6JAkLAAAAANAj7QgAAAAAgDFdN4Jh+wFMUDcClbAAAAAAAH2ShAUAAAAA6JF2BAAAAADAmKpuDB3DpFAJCwAAAADQI5WwAAAAAMAaJqgQdXAqYQEAAAAAeiQJCwAAAADQI+0IAAAAAIBxleH7EQx9/wWkEhYAAAAAoEeSsAAAAAAAPdKOAAAAAAAYU6PH0DFMCpWwAAAAAAA9koQFAAAAAOiRdgQAAAAAwJiqbgwdw6RQCQsAAAAA0COVsAAAAADAmBqNoWOYFCphAQAAAAB6JAkLAAAAANAj7QgAAAAAgDVNUj+AgamEBQAAAADokSQsAAAAAECPtCMAAAAAAMbU6DF0DJNCJSwAAAAAQI8kYQEAAAAAeqQdAQAAAAAwpqobQ8cwKVTCAgAAAAD0SCUsAAAAALCGCSpEHZxKWAAAAACAHknCAgAAAAD0SDsCAAAAAGBcZfh+BEPffwGphAUAAAAA6JEkLAAAAABAj7QjAAAAAADG1OgxdAyTQiUsAAAAAECPVMICAAAAAGOqujF0DJNCJSwAAAAAQI8kYQEAAAAAeqQdAQAAAAAwpkZj6BgmhUpYAAAAAIAeScICAAAAAPRIOwIAAAAAYJx+BAtKJSwAAAAAQI8kYQEAAAAAeqQdAQAAAAAwpkaPoWOYFCphAQAAAAB6pBIWAAAAABhT1Y2hY5gUKmEBAAAAAHq0SSZhq+oFVdWq6uRZ5rauqiOqas9Z5h44mrtZz/EdMYpvpz7vAwAAAABs/DbJJGySQ0bPd6+q+8+Y2zrJq5LsOct5DxzN9ZqEBRjUxRdn830emy223yY3ududM/XlL82+7qqrsvlfHpAtbn7TbHHHnTP1oQ+OTS97/zHZYpfbZoub3zSbH3xgsnLlIgQPACwVz7v4xHz7KxutbwAAGlNJREFUZ8fl8h+/Oy+/4HtzrqvW8qZzv5ELTnxvzjr56Lzo1z8am3/U5Wfn5FM/kEtO/Nd89MzP5ubXXd136ABLRg08Jskml4StqvsmuWeSz44OPWvAcAA2Opu/4G/SbnXrrLzg4lz3j2/O5vs9LVm+fI11mx35qmT5JVl59nm55tj/yOYv/NvUaaclSeqkk7LZSw7NNR/9RFaedW5y/nnZ7DVHLvZHAQAm2Pmbb5vX3PpP8unt77DWdc9ZfnIevOK83OOuz8wjdts3z7/4J3nE5eckSW5x7ZV5/1lfzN/d9sG53e6H5DfLtshbz/36YoQPAOtlk0vC5vdJ15cl+VaSZ1TV1klSVbskuXg0/6pRS4BWVcdU1RFJ3jSa++W0uT1H5z69qr5YVRdU1VVVdVpVvaGqtpkZQFXdv6o+XVXLq+rqqjqjqt62tqCr6i5VdWZVfbeqbrmBXwOA2a1YkalP/leue9WRydZbZ9Xj90m7xz2z7FOfXGPpsg99INcd9opku+3SHvCArNrniVl23Ie7uY8cm+v3fUra/e6XbL99rj/s8Cw79oNrXAMAYL4+dbM75nPb3yG/XXaTta7b/9Kf5W23vFcu3nzrnLHFzXL0DnfLfpf9PEmyz2/PzA+2vmWO326XXDW1eV5z6z/JE357ZrZcdd1ifAQAWGebVBK2qrZKsl+S/2mtnZzk6CQ3TfLU0ZILkuw9en1Ukj8djdck+bck7xjN7Ttt7oejY7sl+Vy6JO/eSd6W5GlJPj0jhr2SfCPJzklenOTRSV6b5FZrifuh6RLGJyZ5WGvt1+v94QHWQZ1+erLttsltb3vDsVW775E69ZTxhZddlrrwwrQ99vj9uj1+v65OOzVt9z3Gr/GrXyVXXNHvBwAAmOEuV1+Wk7fa8Yb3p2y1Y+569aVJkrtefVlOmTZ3zhbb5dqayq4rf7vocQJMmkpSNfAY+ouwgDYbOoD19JQk26dLsCbJcemSpc9K8v7W2sqq+sFo7tzW2nemn1xV54xe/qi1dtb0udbaa6etqyT/neS0JF+rqnu01k4cTf9LknOS3L+1Nr3Z0PtmC7iqDhjF+54kh7bWVq3H513tJklyxi9+MY9TgaVkm5NPyi5bbpVTT/l90vU2K1dm2UUX5VfTjm1+4QXZI8mp55zT/cuWZMcrVuTmF16YX5xySu500UW59Iorcunqc669NvdO8vMf/CDX3eIWi/iJgE3Nlb9bkXPOOScrV1ySVVddOXQ4wCagXb8yraay6qo12yclybarrs1vr/1dVo2+Ff/tdVdn2+uuzqqrlmfra6/IuZttPXbu5VObZeurLsl8vvEClpZr2w37Xqy9JB8WwKaWhH1WkquSfCRJWmsrquqjSQ6uqt1aa6fP98JVdcd0Fa0PT3LLjCfb75rkxKq6c5Jdkxw2IwE7l5cneX6Sl7bW3jrf2JLcLkme9pQnbsAlgKXgj5N8Jcl9/nj3G469Pck1SV7ysY/ecOxmSS5Lsue99sjq2tYXp9u98Cl/vHv+K8lXvvfdvONVhydJdkiyPMmDHvmwrOj9UwAAS8mqJNcnuWb5KbPOr0iy5ZmfyTWj91sluSLJNT/7SC5Pss2Mc7dLctk5X75hPcBcLvz9y9sl+dGcC2EBbDJJ2Kq6U5KHJPlY97ZuNpr6zyQHJzkkyd/P89rbpmsxcHWSw5P8PMmV6f4Qfjzdv/NJsrr869x1vPQBSc7LKGm8Ab6W5AlJfpX4vwQwt62SrW6WfPtvkr3+JbkoSQ5Mjj6ta63yidXrfpPk2uSr70oO/YvRfzZemrx2Rff/kHfukbzofsnN3pEckSTvSu57TfKGFcmfL/6nAgAm2WOTI1ckFx2ZvGu2+WXJh96VHPvg0ebM/5D81fbd92qH/Vny1Dslj/q75DlJ8tLkNtskn6mu9dzK2a4HMM1N0v198rWhA9k4VYZvCDD0/RfOJpOETZdkrXQtCZ4yy/yBVXX4PK/98CS3SbJna+2GP3jTEr2rrd7067ZZN3una5nwjap6RGvt7PkE11r7bZJPzedcYAmq+q93Jvu/s6vEf0SSO98/eU9r7ZIZ6445IHnmAckn01X875nkQa21U1P19iRfa8kbkvwiyVuTvK+1NnuJCgDA+qraLN33pFckuawlZyS5Nq1dP2Pde/8seXbrWsBtn+SJSZ7VWjslVRcneVHrkihfT/KSJB87sbUfBmDdqIBlUWwSG3NV1bIkB6b7R/lhs4x/SvIH6TbJWv3Tzq3WvNKcc23G/GrPHVvU2s9HMRxSVVusQ+hnJ3nw6LrfqKrd1uEcgA311+l+sLQ8XfL06WntklQ9M1XTk6ivTHJpuk0NP5bkBWnt1CRJayel61DwqXTV/xcmefWifQIAYCk4PF27uYPStXK7KslfpOrBqZreAend6arUTk+34fE/p7XjkyTdpsf7p9u745IkO6X7QTQAG2jwTbnqhi1MJkK11m581cCq6nHpfpX2/7TW3jjL/E7pkgSfb609qarOStda4AXpEgyXtNbOqqo9k5yQ5F+TvD/JtUl+lq78/PR0G24dOTr+zCT3SbJbkoNba8eM7rXXKJZT0iU3zkmyc5K9WmvPHK05IsmrktyitXbJKL7j0yVFHtlaO3kBvzwAAAAAsCCq6u5JTv7Kt36UP7rr3QaN5WennZpHPPBeSbL7pv6bmZtEJWy6DbmuSffrJ2sY/YrtJ5I8rqpuNVp/ZboKrv/JqKdha+2rSf4hyeOTfHM0d5/W2vIkjx2d88EkR6fr//70We51fLretBek2+/mC+mqyS6aK/hRfA9PV0X7taq673p8dgAAAABgE7ZJVMICAAAAAP1bXQn7/zaSStiHq4QFAAAAAODGSMICAAAAAPRos6EDAAAAAAA2LlXdGDqGSaESFgAAAACgR5KwAAAAAAA90o4AAAAAABhTSSrD9gOYoG4EKmEBAAAAAPokCQsAAMBGpap2upH5ey9WLABLVm0kY0JIwgIAALCx+XRVbTnbRFXdPcnxixwPAGwQPWEBJlxV7bw+61tr5/QVCwDAOrpVkg8lefL0g1V1pyRfSnLaEEEBwHxJwgJMvrOStPVYv6ynOAAA1tVjkvx3Vb2ltfbi5IYfLH8lyflJHjtkcABLxQR1AxicJCzA5Dsk65eEBQAYVGvtp1W1b5Ljq+qsJMelS8BekeRRrbUrhowPANaXJCzAhGutHTN0DAAA66u19rWqenaS9yU5NMl1Sf68tXbpsJEBwPqThAUAAGBwVbXDLIc/l+QdSZ6ZZO8k16xeJxkL0K+qbgwdw6SQhAVYYqpqWZJHJ7lrkq1mTLfW2msWPyoAgFySuVsoVZLvzzimjz0AmwxJWIAlpKp2TPKNJHdJ903O6p8rTv+GRxIWABjCq6OPPQATShIWYGl5XZKrk9w+ydlJ7p/k0iTPS/K4JH8+XGgAwFLWWjti6BgA+L0aPYaOYVJMDR0AAIvqEUnekuT80ftVrbUzWmsvTfLlJG8eLDIAAACYUJKwAEvLbZOc1Vq7PsmqJNtMm/t0kkcOEhUAwDRV9Zaq+tAccx+sqjctdkwAS05tJGNCSMICLC2XJNl+9Pr8JLtPm9sh2tQAABuHfZJ8cY65LyZ5wiLGAgAbzDfbAEvLD5LcPclnk3wuySur6vIk1yR5fZLvDBgbAMBqf5jkrDnmzk732z0AsMmQhAVYWt6ZZNfR61ckeUCSfx+9PyPJC4cICgBght8lud0cczun22gUgB5tDN0Ahr7/QpKEBVhCWmtfTrcBV1prF1fVvdK1JGhJftpau27I+AAARr6d5O+q6rjW2rWrD1bV5kkOTfKtwSIDgHmQhAVYwlprLclJQ8cBADDDa5N8PcnJVXVUkvPStSA4JMntkzxvwNgAYL1JwgIsQVV193TfwGw5c6619vHFjwgA4Pdaa9+tqn2S/EuSN0ybOiPJPq217w0TGcDSUUlq4H4A2hEAsEmqql2T/GeSe6w+NGNJS7JsUYMCAJhFa+34JHeqqt2S3CLJxa210wcOCwDmRRIWYGn5v0luna6X2mlJrhk2HACAtRslXiVfARZZjR5DxzApJGEBlpY/SfKc1tpHhg4EAODGVNXuSe6aZKuZc621f1/8iABgfiRhAZaWi5P8duggAADWpqq2TvKpJA9P1y5pdSlUm7ZMEhaATcbU0AEAsKjeneQ5QwcBAHAjXpFklyQPTZeA3TfJI5N8PF1rgnsPFhnAUlHdxlxDjgnqRqASFmApaa29qar+qap+kOTzSS5dc0l76wChAQBM94Qk/5jkW6P357TWfpjkK1V1bJK/SvK8oYIDgPUlCQuwhFTV/ZMcmGSHJPeaZUlLIgkLAAxtlyQ/ba1dX1UtydbT5j6U5KhIwgKwCZGEBVha3pnkkiSHJDktyTXDhgMAMKvfJNlm9PrXSXZL8s3R+82nzQHAJkESFmBpuXuSZ7TWPjV0IAAAa3FSkjsn+UKSE5IcVlWnp/sB8iuT/GTA2ABgvUnCAiwt52SiWpsDABPqqHTVr0ny8nRVsF8bvf9NkscMERQAzJckLMDS8oYkL6mq41trVw8dDADAbFpr/zHt9S+r6s5JHp6uf/23WmszNxcFYIFVdWPoGCaFJCzA0nLvJH+Y5IyqOiHJzG9gWmvthYsfFgDA71XVQ5L8sLW2Iklaa79L8unR3DZV9ZDW2teHjBEA1ockLMDS8rfTXu8/y3xLIgkLAAzthCR/muR7s8zdZTS/bFEjAlhiavQYOoZJIQkLsIS01qaGjgEAYB2s7bvuzZOsWqxAAGAhSMICLBFVtWW63YQ/1lr7wdDxAABMV1XbJbnZtEO3rqqdZyzbKsmBSS5ctMAAYAFIwgIsEa21q6vq0CRfGDoWAIBZHJruB8ZJ1yLpE3OsqySvX5SIAJYwG3MtLElYgKXltCR3SGIjCwBgY/PFJCvSJVnfmOQdSc6ZsWZlkpNaa19b5NgAYINIwgIsLa9J8saq+mZr7YyhgwEAWK219u0k306SqtomyXtba+cPGxUALAxJWICl5eAkWyc5rapOTHJBul/3W6211p4wSGQAAL93+yRbzDZRVbdP8qrW2iGLGxLA0lJZ+y6JixXDpLBLNsDSco8k1yQ5L8mOSXZPsseMAQAwtAOT3GKOuZ1G8wCwyVAJC7CEtNZ2GToGAIB1sLbipx3S9YYFgE2GJCwAAACDq6qHJNlz2qFnV9XeM5ZtleQJSU5drLgAliz9CBaUJCzAElNVmyf5yySPSNeS4JIkX07ywdbatUPGBgAsaQ9L8qrR65bk2XOsOzvJ3y5KRACwQCRhAZaQqto+yVeS3DvJ75JcmOSBSfZL8tdV9YjW2uUDhggALF1vTPLOdHVPv06yV5IfzlizsrW2oqrm6hcLwAKp0WPoGCaFjbkAlpbXJfmjJE9vrd20tbZba+2mSZ42Ov66QaMDAJas1tpVrbXlrbVLktwhyVdH75e31pYnuTTJQ6rqY0nOHTRYAFhPKmEBlpYnJnlla+2j0w+21v6zqnZO8uIkzx8kMgCAkdba2atfV9WuSQ5JcmCSP0hyTZKPDRQaAMyLJCzA0nKLJCfOMfeTJDstYiwAALOqqi2TPDXJs5I8OF2LgpbkLUneMKqMBaBPldTQ3QCGvv8C0o4AYGk5L8mfzTH3oCTnL2IsAABjqup+VfWedH3rj0nXx/6YJI9L9634pyVgAdgUqYQFWFqOS3JYVV2R5P2tteVVtWOSA5Iclq66BABg0VXViUnuPnr77SRHJzmutfa70eaiALDJkoQFWFqOSHKvJG9O8qaqui7dvwWV5PjRPADAEHZP13Lgs0le1lo7deB4AJa0yvDdAIa+/0KShAVYQlprK5PsXVV7JXl4kh2SLE/yldbalwYNDgBY6l6U5OB0rQceW1XfS3JUut/kAYBNmiQswBLUWjs+XeUrAMBGobX29iRvr6r7ptuQ6xlJ/m+St6Wrjm2jAQCbHElYgAk36q+2rlpr7Z69BQMAcCNaa99P8v2qOjTJU9MlZJ+S7rdSj6qqf01yjA26ABbBJPUDGNjU0AEA0LtL07UcWNtYma4P2+4DxQgAMKa1dnVr7QOttT2T3DnJG5JsneRNSX41ZGwAsL5UwgJMuNE3LrOqqs2S/K8kr0z3633HLlJYAADrrLV2RpLDqurwJI9JcsjAIQFMvBo9ho5hUqiEBViiquqpSU5N8o4kP0lyn9baXwwbFQDA3Fprq1prn2mt7Tt0LACwPiRhAZaYqtqzqr6bbqfhy5M8qrW2V2vtxwOHBgAAABNJOwKAJaKq9kjyj0n2SvLLJPu31j4ybFQAAABsjKq6MXQMk0ISFmDCVdXtkrw2yf7pNul6UZL3tNauHTQwAAAANlpnnPGLoUPYKGJYKNVaGzoGAHpUVVcluUmSLyR5Y5Ir1ra+tfbDxYgLAACAjU9V3SbJT5PcdOhYRq5IcpfW2vlDB7IhJGEBJlxVrZr2dm1/6VeS1lpb1nNIAAAAbMRGidibDx3HyGWbegI20Y4AYCk4eOgAAAAA2HSMkp6bfOJzY6ISFgAAAACgR1NDBwAAAAAAMMkkYQEAAAAAeiQJCwAAAADQI0lYAAAAAIAeScICAPSoqg6qqjZtXFdV51bV+6rqDxcphj1H995z2rFjquqseVzrr6vqoAUMb/q1W1UdcSNrdhmtW+8Ypn0dnjLfGNdyzT0X6poAAEweSVgAgMVxcJI/TfLIJO9Nsl+Sb1TVNgPF85okT5rHeX+d5KCFDQUAACbbZkMHAACwRJzcWvv+6PUJVbUsySuSPDHJh2Y7oaq2SnJ1a60tdDCttTMW+poAAMDsVMICAAzjO6Pn2ydjbQseVVVHV9XFSa5MssVofreqOraqfl1VK6vqtKr6m5kXraq7VNUXqurKqrqkqt6T5KazrFujHUFVTVXV86vqx1V1VVX9pqq+U1X7jObPSnL3JA+d1l7hrGnnb1dVb66qX1bVNVV1XlW9bWa172jde6tqeVWtGMV75/l+IavqTqP2DqePPvd5VfXpqtpjjlO2rKq3VNWFo8/5taq61yzXvW9VfaqqLq2qq6vqR1X1tPnGCQDA0qUSFgBgGHcaPV884/jRST6b5C+SbJPk2qq6W5JvJTknyd8luTDJXkneXlU7tdaOTJKqulWSryW5Nl3bgIuSPDPJO9cxpmOSHJDkqCSvTHJNknsn2WU0/6Qk/5nkt6PrJ8nK0b23Ht37tklen+TEdAnbVyfZo6r+vLXWqqqS/FeSB47m/ifJg5J8fh1jnM1tkixP8rJ0X88dkhyY5LtVda/W2s9mrH99kh8meXaS7ZMckeSro7Vnjj7Pw5J8Icl3kzxv9JmfkeS4qtq6tXbMBsQLAMASIwkLALA4llXVZkm2TPLQJIcnuSLJp2as+0pr7bnTD1TVW0Zr/6y1dvno8JeqaoskL6uqt7fWLktyaJJbJLlXa+0no3Wfr6ovJtl5bcFV1YPTJX5f11o7fNrUF1a/aK39qKquSnJ5a+07My7xgiT3SHL/aW0XvlJV56VL3O6dLtG6V5KHJXlha+3t0z7LNUlet7YY59Ja+3qSr0/7LMvSJbJPSfLcJC+eccrFSZ60us1DVX0zyelJ/j7Jc0Zr3jU6/+GttetGx46vqp2SvL6q/r21tmo+8QIAsPRoRwAAsDi+k65C9Yokn0lXzfro1tpFM9Z9bPqbqtoyySOSfCLJlVW12eqR5HPpkroPGC1/WJJTpiVgVzt2HeJ79Oj5X9bx88z0uCQnJ/nxjBiPT9KS7DktxmTNPrjrEuOsRvc6rKpOHSVzr0tXxbtbkrvOcsqx0/vsttbOTldp/LDR9e6U5C6rY5zla/4HSf5ovvECALD0qIQFAFgcf5nktHQJwotaaxfMsW7m8R3T/Z/t+aMxm52mrf3lLPMXrkN8t0hy/Tqunc2t0rVYuHaO+ekxXtdaWz5jfr73TZK3JPmbJP+YriXCZUlWJfm3JFvNsn62e12Y5J6j17caPb95NGaz0xzHAQBgDZKwAACL47Rpv6a/Nm3G+8vSJUc/kLmrVFcnXpcnufUs87Mdm+niJMtGa+dKEK/NJUmuSnLIWuaTLsbNqmrHGYnYdYlxLgck+ffW2mHTD45aB/xmlvVzfY1Wx7M61n9I8vE57jmzzywAAMxJOwIAgI1Ya+3KJCckuVeSE1tr359lrE4enpDk7lV1zxmX2X8dbrV6Y6y/upF1KzN7delnkuyaZPkcMZ41Lcak2zBsfWOcSxvFdYOqemySP5xj/X6jDcJWr719uo3Cvpoko428Tk9yzzk+y/dba1dsQLwAACwxKmEBADZ+L0zyzSTfqKp3JzkryU3T/fr/41trDx+te1u6StTPVtXhSS5Kl+y8y43doLX2jar6QJLDq+pW6ZKqK9Mlf69srb1jtPSkJM+oqqcnOTPJ1a21k0b3fnKSr1fVW5OcmO4H/jsneVSSf2qtfTfJF9NtovXGqtomyfeTPCjdpmDz9ZkkB1XVT0f3vU+SlyY5d471t0zyiap6b5LtkxyZ5Op0la+rPTfdpmbHJzkmyXlJdkjXY/berbWnbkC8AAAsMZKwAAAbudbaqVV17ySvSPLadEnE36Sr1vzctHUXVtVDk/xzkncnuTLdhl5/m+ST63Crg5L8MMmzRq+vSnJqktdPW/OqdBtTvTddIvjsJLu01n5XVQ9O8rIk/yvJHUbnn5Pky+kSx2mtraqqfdL1cf3fSW6S5L+TPCbJT9f5izLuhel60f59km1Hn2HfdF+r2RyW5H5J3pdkuyTfS/KM1toZqxe01k6oqj9J8vJ0Ceabp2tXcGqS/5hnnAAALFE1bWNYAAAAAAAWmJ6wAAAAAAA9koQFAAAAAOiRJCwAAAAAQI8kYQEAAAAAeiQJCwAAAADQI0lYAAAAAIAeScICAAAAAPRIEhYAAAAAoEeSsAAAAAAAPZKEBQAAAADokSQsAAAAAECPJGEBAAAAAHokCQsAAAAA0CNJWAAAAACAHv1/LgXkag1Y1nIAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<Figure size 1800x960 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# -*-coding:utf-8-*-\n",
    "from sklearn.metrics import confusion_matrix\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "#labels表示你不同类别的代号\n",
    "# labels = ['Normal','Analysis','Backdoor','DoS','Exploits','Fuzzers','Generic','Reconnaissance','Shellcode','Worms']\n",
    "labels = ['Normal','Attack']\n",
    "          \n",
    "y_true = y_true\n",
    "y_pred  = y_pred  \n",
    "\n",
    "tick_marks = np.array(range(len(labels))) + 0.5\n",
    "\n",
    "def plot_confusion_matrix(cm, title='Confusion Matrix', cmap=plt.cm.binary):\n",
    "    plt.imshow(cm, interpolation='nearest', cmap=plt.cm.Blues) \n",
    "    plt.title(title)\n",
    "    plt.colorbar()\n",
    "    xlocations = np.array(range(len(labels)))\n",
    "    plt.xticks(xlocations, labels, rotation=90)\n",
    "    plt.yticks(xlocations, labels)\n",
    "    plt.ylabel('True label')\n",
    "    plt.xlabel('Predicted label')\n",
    "\n",
    "\n",
    "cm = confusion_matrix(y_true, y_pred)  #Confusion matrix\n",
    "np.set_printoptions(precision=2)  \n",
    "cm_normalized = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]  \n",
    "\n",
    "plt.figure(figsize=(15, 8), dpi=120)\n",
    "\n",
    "ind_array = np.arange(len(labels))\n",
    "x, y = np.meshgrid(ind_array, ind_array)\n",
    "\n",
    "for x_val, y_val in zip(x.flatten(), y.flatten()):\n",
    "    c = cm_normalized[y_val][x_val]\n",
    "    if c > 0.001:\n",
    "        plt.text(x_val, y_val, \"%0.2f\" % (c,), color='red', fontsize=7, va='center', ha='center')\n",
    "# offset the tick\n",
    "plt.gca().set_xticks(tick_marks, minor=True)\n",
    "plt.gca().set_yticks(tick_marks, minor=True)\n",
    "plt.gca().xaxis.set_ticks_position('none')\n",
    "plt.gca().yaxis.set_ticks_position('none')\n",
    "plt.grid(True, which='minor', linestyle='-')\n",
    "plt.gcf().subplots_adjust(bottom=0.15)\n",
    "\n",
    "plot_confusion_matrix(cm_normalized, title='Normalized confusion matrix')  \n",
    "#plt.savefig('confusion_matrix.png', format='png')  \n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[437034   6721]\n",
      " [    82  64175]]\n"
     ]
    }
   ],
   "source": [
    "print(cm)  #Confusion matrix"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# # multi-class evaluation indicators"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn import metrics\n",
    "from sklearn.metrics import classification_report\n",
    "\n",
    "target_names = ['Normal','Analysis','Backdoor','DoS','Exploits','Fuzzers','Generic','Reconnaissance','Shellcode','Worms']\n",
    "print(classification_report(y_true,y_pred,target_names=target_names))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "acc = metrics.accuracy_score(y_true,y_pred) \n",
    "f1 = metrics.f1_score(y_true, y_pred,average='weighted')\n",
    "pre = metrics.precision_score(y_true, y_pred, labels=None, pos_label=1, average='weighted')  #DR\n",
    "recall = metrics.recall_score(y_true, y_pred, labels=None, pos_label=1, average='weighted', sample_weight=None)\n",
    "\n",
    "print(\"acc:\",acc)\n",
    "print(\"pre:\",pre)\n",
    "print(\"DR=recall:\",recall)\n",
    "print(\"f1:\",f1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# binary-class evaluation indicators"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "acc: 0.9866085840491957\n",
      "DR: 0.9987238744416951\n",
      "FPR: 0.015145744836677896\n",
      "recall： 0.9987238744416951\n",
      "precision: 0.9051991649740465\n",
      "f1: 0.9496644543591337\n"
     ]
    }
   ],
   "source": [
    "TP=cm[1,1]\n",
    "FP=cm[0,1]\n",
    "FN=cm[1,0]\n",
    "TN=cm[0,0]\n",
    "\n",
    "acc = (TP+TN)/(TP+TN+FP+FN)\n",
    "print(\"acc:\",acc)\n",
    "\n",
    "DR = TP/(TP+FN)  \n",
    "print(\"DR:\",DR)\n",
    "\n",
    "FPR = FP/(FP+TN)  #FAR\n",
    "print(\"FPR:\",FPR)\n",
    "\n",
    "recall =TP/(TP+FN)\n",
    "print(\"recall：\",recall)  \n",
    "\n",
    "precision = TP/(TP+FP)\n",
    "print(\"precision:\",precision)\n",
    "\n",
    "f1 = (2*precision*recall)/(precision+recall)\n",
    "print(\"f1:\",f1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Parameter tuning"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.model_selection import RandomizedSearchCV\n",
    "from sklearn.ensemble import RandomForestClassifier\n",
    "from sklearn import metrics\n",
    "# Hyperparameter grid\n",
    "param_grid = {\n",
    "    'n_estimators': np.linspace(10, 200).astype(int),\n",
    "    'max_depth': [None] + list(np.linspace(3, 30).astype(int)),\n",
    "    'max_features': ['auto', 'sqrt', None],\n",
    "#      'max_features': ['auto', 'sqrt', None] + list(np.arange(0.5, 1, 0.1)),\n",
    "    'max_leaf_nodes': [None] + list(np.linspace(10, 50, 500).astype(int)),\n",
    "    'min_samples_split': [2, 5, 10],\n",
    "    'bootstrap': [True, False]\n",
    "}\n",
    "# Estimator for use in random search\n",
    "estimator = RandomForestClassifier(random_state = RSEED)\n",
    "# Create the random search model\n",
    "rs = RandomizedSearchCV(estimator, param_grid, n_jobs = -1, \n",
    "                        scoring = 'neg_log_loss', cv = 3, \n",
    "                        n_iter = 10, \n",
    "                        verbose = 1, \n",
    "                        random_state=RSEED)  #neg_log_loss\n",
    "# Fit \n",
    "rs.fit(x_train, y_train)\n",
    "\n",
    "print(\"随机搜索-度量记录：\",rs.cv_results_)  \n",
    "print(\"最佳参数：\",rs.best_params_ )\n",
    "print(\"最佳度量值：:\",rs.best_score_)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
